{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# ART for TensorFlow v2 - Callable Class/Function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This notebook demonstrates applying ART with TensorFlow v2 using callable classes or functions to define models. The code follows and extends the examples on www.tensorflow.org."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import warnings\n",
    "warnings.filterwarnings('ignore')\n",
    "import os\n",
    "os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' \n",
    "import tensorflow as tf\n",
    "from tensorflow.keras.layers import Dense, Flatten, Conv2D\n",
    "from tensorflow.keras import Model\n",
    "import numpy as np\n",
    "from matplotlib import pyplot as plt\n",
    "from art.classifiers import TensorFlowV2Classifier\n",
    "from art.attacks import FastGradientMethod, CarliniLInfMethod"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "if tf.__version__[0] != '2':\n",
    "    raise ImportError('This notebook requires Tensorflow v2.')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Load MNIST dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n",
    "x_train, x_test = x_train / 255.0, x_test / 255.0\n",
    "\n",
    "x_train = x_train.astype(np.float32)\n",
    "x_test = x_test.astype(np.float32)\n",
    "\n",
    "x_test = x_test[0:100]\n",
    "y_test = y_test[0:100]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Add a dimension for color channel"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train = x_train[..., tf.newaxis]\n",
    "x_test = x_test[..., tf.newaxis]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create loss object and optimizer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "loss_object = tf.keras.losses.SparseCategoricalCrossentropy()\n",
    "optimizer = tf.keras.optimizers.Adam()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define metrics for training and testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_loss = tf.keras.metrics.Mean(name='train_loss')\n",
    "train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')\n",
    "\n",
    "test_loss = tf.keras.metrics.Mean(name='test_loss')\n",
    "test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tensorflow with callable class"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a custom model class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "class KerasModel(Model):\n",
    "    def __init__(self):\n",
    "        super(KerasModel, self).__init__()\n",
    "        self.conv1 = Conv2D(filters=3, kernel_size=3, activation='relu')\n",
    "        self.flatten = Flatten()\n",
    "        self.dense1 = Dense(10, activation='softmax')\n",
    "\n",
    "    def call(self, x):\n",
    "        x = self.conv1(x)\n",
    "        x = self.flatten(x)\n",
    "        x = self.dense1(x)\n",
    "        return x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create callable model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "model = KerasModel()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create input pipelines for training and testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train)).shuffle(10000).batch(32)\n",
    "test_ds = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define the training step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "@tf.function\n",
    "def train_step(images, labels):\n",
    "    with tf.GradientTape() as tape:\n",
    "        predictions = model(images)\n",
    "        loss = loss_object(labels, predictions)\n",
    "    gradients = tape.gradient(loss, model.trainable_variables)\n",
    "    optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n",
    "\n",
    "    train_loss(loss)\n",
    "    train_accuracy(labels, predictions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define the testing step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [],
   "source": [
    "@tf.function\n",
    "def test_step(images, labels):\n",
    "    predictions = model(images)\n",
    "    t_loss = loss_object(labels, predictions)\n",
    "\n",
    "    test_loss(t_loss)\n",
    "    test_accuracy(labels, predictions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fit the model on training data and collect metrics for training and testing."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1, Loss: 0.34, Accuracy: 90.76, Test Loss: 0.10, Test Accuracy: 95.00\n",
      "Epoch 2, Loss: 0.25, Accuracy: 93.10, Test Loss: 0.10, Test Accuracy: 96.00\n",
      "Epoch 3, Loss: 0.21, Accuracy: 94.26, Test Loss: 0.09, Test Accuracy: 96.67\n"
     ]
    }
   ],
   "source": [
    "epochs = 3\n",
    "\n",
    "for epoch in range(epochs):\n",
    "    for images, labels in train_ds:\n",
    "        train_step(images, labels)\n",
    "\n",
    "    for test_images, test_labels in test_ds:\n",
    "        test_step(test_images, test_labels)\n",
    "\n",
    "    template = 'Epoch {}, Loss: {:4.2f}, Accuracy: {:4.2f}, Test Loss: {:4.2f}, Test Accuracy: {:4.2f}'\n",
    "    print(template.format(epoch + 1,\n",
    "                          train_loss.result(),\n",
    "                          train_accuracy.result() * 100,\n",
    "                          test_loss.result(),\n",
    "                          test_accuracy.result() * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Evaluate model accuracy on test data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on test data: 98.00%\n"
     ]
    }
   ],
   "source": [
    "y_test_pred = np.argmax(model(x_test), axis=1)\n",
    "accuracy_test = np.sum(y_test_pred == y_test) / y_test.shape[0]\n",
    "print('Accuracy on test data: {:4.2f}%'.format(accuracy_test * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a ART Tensorflow v2 classifier for the Tensorflow custom model class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "classifier = TensorFlowV2Classifier(model=model, nb_classes=10, input_shape=(28, 28, 1), loss_object=loss_object, \n",
    "                                    clip_values=(0, 1), channel_index=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Fast Gradient Sign Method attack"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a ART Fast Gradient Sign Method attack."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "attack_fgsm = FastGradientMethod(classifier=classifier)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate adversarial test data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "x_test_adv = attack_fgsm.generate(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Evaluate accuracy on adversarial test data and calculate average perturbation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on adversarial test data: 2.00%\n",
      "Average perturbation: 0.16\n"
     ]
    }
   ],
   "source": [
    "y_test_pred = np.argmax(model(x_test_adv), axis=1)\n",
    "accuracy_test_adv = np.sum(y_test_pred == y_test) / y_test.shape[0]\n",
    "perturbation = np.mean(np.abs((x_test_adv - x_test)))\n",
    "print('Accuracy on adversarial test data: {:4.2f}%'.format(accuracy_test_adv * 100))\n",
    "print('Average perturbation: {:4.2f}'.format(perturbation))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Visualise the first adversarial test sample."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 288x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.matshow(x_test_adv[0, :, :, 0])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Carlini&Wagner Infinity-norm attack"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a ART Carlini&Wagner Infinity-norm attack."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [],
   "source": [
    "attack_cw = CarliniLInfMethod(classifier=classifier, eps=0.3, max_iter=100, learning_rate=0.01)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate adversarial test data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_test_adv = attack_cw.generate(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Evaluate accuracy on adversarial test data and calculate average perturbation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on adversarial test data: 21.00%\n",
      "Average perturbation: 0.02\n"
     ]
    }
   ],
   "source": [
    "y_test_pred = np.argmax(model(x_test_adv), axis=1)\n",
    "accuracy_test_adv = np.sum(y_test_pred == y_test) / y_test.shape[0]\n",
    "perturbation = np.mean(np.abs((x_test_adv - x_test)))\n",
    "print('Accuracy on adversarial test data: {:4.2f}%'.format(accuracy_test_adv * 100))\n",
    "print('Average perturbation: {:4.2f}'.format(perturbation))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Visualise the first adversarial test sample."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 288x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.matshow(x_test_adv[0, :, :, 0])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Tensorflow with custom function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Reshape dataset in feature vectors becasue the model in this example requires feature vectors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "x_train = x_train.reshape((60000, 784))\n",
    "x_test = x_test.reshape((100, 784))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create input pipelines for training and testing"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train)).shuffle(10000).batch(32)\n",
    "test_ds = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create variables and keep track of them"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "W = tf.Variable(initial_value=tf.random.normal(shape=(784, 10)), name=\"W\")\n",
    "b = tf.Variable(tf.zeros(shape=(10)), name=\"b\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define a function representing the model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "@tf.function\n",
    "def forward(x):\n",
    "    x = tf.matmul(x, W) + b\n",
    "    denominator = tf.expand_dims(tf.reduce_sum(tf.exp(x), axis=1), axis=1)\n",
    "    softmax = (1.0 / denominator) * tf.exp(x)\n",
    "    return softmax"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define the training step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "@tf.function\n",
    "def train_step(images, labels):\n",
    "    with tf.GradientTape() as tape:\n",
    "        predictions = forward(images)\n",
    "        loss = loss_object(labels, predictions)\n",
    "    gradients = tape.gradient(loss, [W, b])\n",
    "    optimizer.apply_gradients(zip(gradients, [W, b]))\n",
    "\n",
    "    train_loss(loss)\n",
    "    train_accuracy(labels, predictions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Define the testing step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "@tf.function\n",
    "def test_step(images, labels):\n",
    "    predictions = forward(images)\n",
    "    t_loss = loss_object(labels, predictions)\n",
    "\n",
    "    test_loss(t_loss)\n",
    "    test_accuracy(labels, predictions)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Fit the model on training data and collect metrics for training and testing."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1, Loss: 0.59, Accuracy: 88.81, Test Loss: 0.42, Test Accuracy: 93.25\n",
      "Epoch 2, Loss: 0.62, Accuracy: 88.03, Test Loss: 0.50, Test Accuracy: 91.80\n",
      "Epoch 3, Loss: 0.61, Accuracy: 87.91, Test Loss: 0.50, Test Accuracy: 91.17\n"
     ]
    }
   ],
   "source": [
    "epochs = 3\n",
    "\n",
    "for epoch in range(epochs):\n",
    "    for images, labels in train_ds:\n",
    "        train_step(images, labels)\n",
    "\n",
    "    for test_images, test_labels in test_ds:\n",
    "        test_step(test_images, test_labels)\n",
    "\n",
    "    template = 'Epoch {}, Loss: {:4.2f}, Accuracy: {:4.2f}, Test Loss: {:4.2f}, Test Accuracy: {:4.2f}'\n",
    "    print(template.format(epoch + 1,\n",
    "                          train_loss.result(),\n",
    "                          train_accuracy.result() * 100,\n",
    "                          test_loss.result(),\n",
    "                          test_accuracy.result() * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Evaluate model accuracy on test data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on test data: 88.00%\n"
     ]
    }
   ],
   "source": [
    "y_test_pred = np.argmax(forward(x_test), axis=1)\n",
    "accuracy_test = np.sum(y_test_pred == y_test) / y_test.shape[0]\n",
    "print('Accuracy on test data: {:4.2f}%'.format(accuracy_test * 100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a ART Tensorflow v2 classifier for the Tensorflow custom model function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "classifier = TensorFlowV2Classifier(model=forward, nb_classes=10, input_shape=(28, 28, 1),\n",
    "                                    loss_object=loss_object, clip_values=(0, 1), channel_index=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Fast Gradient Sign Method attack"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a ART Fast Gradient Sign Method attack."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "attack_fgsm = FastGradientMethod(classifier=classifier)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate adversarial test data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "x_test_adv = attack_fgsm.generate(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Evaluate accuracy on adversarial test data and calculate average perturbation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on adversarial test data: 17.00%\n",
      "Average perturbation: 0.15\n"
     ]
    }
   ],
   "source": [
    "y_test_pred = np.argmax(forward(x_test_adv), axis=1)\n",
    "accuracy_test_adv = np.sum(y_test_pred == y_test) / y_test.shape[0]\n",
    "perturbation = np.mean(np.abs((x_test_adv - x_test)))\n",
    "print('Accuracy on adversarial test data: {:4.2f}%'.format(accuracy_test_adv * 100))\n",
    "print('Average perturbation: {:4.2f}'.format(perturbation))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Visualise the first adversarial test sample."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 288x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.matshow(x_test_adv[0, :].reshape((28, 28)))\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Carlini&Wagner Infinity-norm attack"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Create a ART Carlini&Wagner Infinity-norm attack."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "attack_cw = CarliniLInfMethod(classifier=classifier, eps=0.3, max_iter=100, learning_rate=0.01)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Generate adversarial test data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:5 out of the last 429 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:6 out of the last 430 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:7 out of the last 431 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:8 out of the last 432 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:9 out of the last 434 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:9 out of the last 11 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:5 out of the last 27 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:6 out of the last 28 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:7 out of the last 31 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:7 out of the last 11 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:5 out of the last 25 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:6 out of the last 27 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:7 out of the last 29 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:7 out of the last 11 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:5 out of the last 24 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:6 out of the last 25 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "WARNING:tensorflow:7 out of the last 26 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:8 out of the last 27 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:9 out of the last 28 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:10 out of the last 29 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:5 out of the last 27 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:5 out of the last 11 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:5 out of the last 24 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:6 out of the last 25 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:6 out of the last 11 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:5 out of the last 25 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:6 out of the last 26 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n",
      "WARNING:tensorflow:7 out of the last 27 calls to <function forward at 0x7f55e4229158> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings is likely due to passing python objects instead of tensors. Also, tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. Please refer to https://www.tensorflow.org/tutorials/customization/performance#python_or_tensor_args and https://www.tensorflow.org/api_docs/python/tf/function for more details.\n"
     ]
    }
   ],
   "source": [
    "%%capture\n",
    "x_test_adv = attack_cw.generate(x_test)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Evaluate accuracy on adversarial test data and calculate average perturbation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Accuracy on adversarial test data: 39.00%\n",
      "Average perturbation: 0.006367\n"
     ]
    }
   ],
   "source": [
    "y_test_pred = np.argmax(forward(x_test_adv), axis=1)\n",
    "accuracy_test_adv = np.sum(y_test_pred == y_test) / y_test.shape[0]\n",
    "perturbation = np.mean(np.abs((x_test_adv - x_test)))\n",
    "print('Accuracy on adversarial test data: {:4.2f}%'.format(accuracy_test_adv * 100))\n",
    "print('Average perturbation: {:4.6f}'.format(perturbation))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Visualise the first adversarial test sample."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 288x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.matshow(x_test_adv[0, :].reshape((28, 28)))\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "venv36_TF210_keras231",
   "language": "python",
   "name": "venv36_tf210_keras231"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
